#include "FunctionInterpreter.h"
#include "Object/NewObject.h"

#include "ScriptRuntime/ByteCode/ByteCodeFile.h"
#include "ScriptRuntime/ByteCode/ByteCodeReader.h"
#include "ScriptRuntime/ByteCode/ByteCodeInstruction.h"


#include "ScriptRuntime/ScriptSubsystem.h"

#include "ScriptRuntime/Type/GSharpClass.h"
#include "ScriptRuntime/Type/ScriptContent.h"
#include "ScriptRuntime/Type/GSharpBoxedValue.h"
#include "ScriptRuntime/ScriptInnerType.h"

#include "Reflection/Function.h"
#include "Reflection/FunctionList.h"
#include "Reflection/Property.h"
#include "Reflection/PropertyList.h"
#include "Reflection/PointerType.h"
#include "Reflection/NullObjectPtr.h"

#include "Framework/Application.h"

#include "ScriptRuntime/Debug/Breakpoint.h"

#include "Output/Console.h"


#define COMPARE_NUM(CompareOperator,NumberType) \
NumberType Right = AnyCast<NumberType>(ValueStack.top()); \
ValueStack.pop();  \
NumberType Left = AnyCast<NumberType>(ValueStack.top()); \
ValueStack.pop(); \
ValueStack.push((bool)(Left CompareOperator Right));  \
GoNextLine(); \
break; 


#define BRANCH_COMPARE(CompareOperator) \
auto Right = ValueStack.top(); \
ValueStack.pop();  \
auto Left = ValueStack.top(); \
ValueStack.pop(); \
uint32_t JumpTargetLine; \
*ScriptFileReader>>JumpTargetLine; \
CType* LeftType=Left.GetType(); \
if(LeftType== CGetType<double>()() ) \
{ \
    /*if one of them is double, anothor must be double*/ \
    double LeftValue = AnyCast<double>(Left);  \
    double RightValue = AnyCast<double>(Right); \
    if(LeftValue CompareOperator RightValue) \
    { \
        GoLine(JumpTargetLine); \
    }else \
    {  \
        GoNextLine(); \
    } \
    break; \
} \
if(LeftType== CGetType<float>()()) \
{ \
    /*if one of them is float, anothor must be float*/ \
    float LeftValue = AnyCast<float>(Left); \
    float RightValue = AnyCast<float>(Right); \
    if(LeftValue CompareOperator RightValue) \
    { \
        GoLine(JumpTargetLine); \
    }else \
    {  \
        GoNextLine(); \
    } \
    break; \
} \
CType* RightType=Right.GetType(); \
static CType* Uint64Type= CGetType<uint64_t>()(); \
if(RightType==Uint64Type && LeftType ==Uint64Type  ) \
{ \
    uint64_t LeftValue= AnyCast<uint64_t>(Left); \
    uint64_t RightValue= AnyCast<uint64_t>(Right); \
    if(LeftValue CompareOperator RightValue) \
    { \
        GoLine(JumpTargetLine); \
    }else \
    {  \
        GoNextLine(); \
    } \
    break; \
} \
int64_t LeftValue; \
if(Left.GetType()== CGetType<int64_t>()()) \
{ \
    LeftValue = AnyCast<int64_t>(Left); \
} \
else if(Left.GetType()== CGetType<int32_t>()() ) \
{ \
    LeftValue = AnyCast<int32_t>(Left); \
} \
else if(Left.GetType()== CGetType<int16_t>()() ) \
{ \
    LeftValue = AnyCast<int16_t>(Left); \
} \
else if(Left.GetType()== CGetType<int8_t>()() ) \
{ \
    LeftValue = AnyCast<int8_t>(Left); \
} \
else if(Left.GetType()== CGetType<uint64_t>()()) \
{ \
    LeftValue = AnyCast<uint64_t>(Left); \
} \
else if(Left.GetType()== CGetType<uint32_t>()()) \
{ \
    LeftValue = AnyCast<uint32_t>(Left); \
} \
else if(Left.GetType()== CGetType<uint16_t>()()) \
{ \
    LeftValue = AnyCast<uint32_t>(Left); \
} \
else if(Left.GetType()== CGetType<uint8_t>()()) \
{ \
    LeftValue = AnyCast<uint8_t>(Left); \
} \
else \
{ \
    assert(false && " unsupported type"); \
} \
int64_t RightValue; \
if(Right.GetType()== CGetType<int64_t>()()) \
{ \
    RightValue = AnyCast<int64_t>(Right); \
} \
else if(Right.GetType()== CGetType<int32_t>()() ) \
{ \
    RightValue = AnyCast<int32_t>(Right); \
} \
else if(Right.GetType()== CGetType<int16_t>()() ) \
{ \
    RightValue = AnyCast<int16_t>(Right); \
} \
else if(Right.GetType()== CGetType<int8_t>()() ) \
{ \
    RightValue = AnyCast<int8_t>(Right); \
}   \
else if(Right.GetType()== CGetType<uint64_t>()()) \
{ \
    RightValue = AnyCast<uint64_t>(Right); \
} \
else if(Right.GetType()== CGetType<uint32_t>()()) \
{ \
    RightValue = AnyCast<uint32_t>(Right); \
} \
else if(Right.GetType()== CGetType<uint16_t>()()) \
{ \
    RightValue = AnyCast<uint32_t>(Right); \
} \
else if(Right.GetType()== CGetType<uint8_t>()()) \
{ \
    RightValue = AnyCast<uint8_t>(Right); \
} \
else \
{ \
    assert(false && " unsupported type"); \
} \
if(LeftValue CompareOperator RightValue) \
{ \
    GoLine(JumpTargetLine); \
}else \
{  \
    GoNextLine(); \
} \
break;


#define CONVERT_INNERTYPE(FromType,ToType) \
    CAny& AnyValue=ValueStack.top(); \
    FromType Value = AnyCast<FromType>(AnyValue); \
    AnyValue= (ToType)Value; \
    GoNextLine(); \
    break;

#define Negative_NUMBER(NumberType) \
    NumberType Value = AnyCast<NumberType>(ValueStack.top()); \
    ValueStack.top()= -Value; \
    GoNextLine(); \
    break; 

#define CALCULATE_NUMBER(Operator, Type) \
    Type Right =AnyCast<Type>(ValueStack.top()); \
    ValueStack.pop(); \
    CAny& LeftAny = ValueStack.top(); \
    Type Left = AnyCast<Type>(ValueStack.top()); \
    LeftAny= (Left Operator Right); \
    GoNextLine(); \
    break;


CFunctionInterpreter::CFunctionInterpreter(
    const TVector<std::shared_ptr<CBreakpointSlot>> &BreakpointSlots
    ,CScriptFunction* InFunction)
    :BreakpointSlots(BreakpointSlots)
    ,Function(InFunction)
{
}

CAny CFunctionInterpreter::RunScript()
{
    ScriptFileReader= std::make_shared<CByteCodeReader>(ScriptFile,ByteCodeStart);

    while(LineIndex < FunctionLines.Num())
    {
        //check is there a breakpoint
        if(BreakpointSlots[LineIndex]&&BreakpointSlots[LineIndex]->State==CBreakpointSlot::EBreakpointState::Enabled)
        {
            //hit the breakpoint
            RScriptSubsystem::Get()->WaitBreakpointOver();
            //assert(false && "Hit break point");
        }

        if(!ExecInstruction())
        {
            return CAny();
        }
            
    }

    if(ValueStack.empty())
    {
        return CAny();
    }
    auto Ret= ValueStack.top();
    ValueStack.pop();
    return Ret;
}

bool CFunctionInterpreter::ExecInstruction()
{   


    {
        uint32_t LineOffset= FunctionLines[LineIndex];
        ScriptFileReader->SetOffset(LineOffset+ ByteCodeStart);
    }
    EByteCodeInstructions Instruction;
    *ScriptFileReader>>Instruction;

    switch (Instruction)
    {

    case EByteCodeInstructions::Add_i8:
    {
        CALCULATE_NUMBER(+,int8_t);
    }
    case EByteCodeInstructions::Add_i16:
    {
        CALCULATE_NUMBER(+,int16_t);
    }
    case EByteCodeInstructions::Add_i32:
    {
        CALCULATE_NUMBER(+,int32_t);
    }
    case EByteCodeInstructions::Add_i64:
    {
        CALCULATE_NUMBER(+,int64_t);
    }
    case EByteCodeInstructions::Add_u8:
    {
        CALCULATE_NUMBER(+,uint8_t);
    }
    case EByteCodeInstructions::Add_u16:
    {
        CALCULATE_NUMBER(+,uint16_t);
    }
    case EByteCodeInstructions::Add_u32:
    {
        CALCULATE_NUMBER(+,uint32_t);
    }
    case EByteCodeInstructions::Add_u64:
    {
        CALCULATE_NUMBER(+,uint64_t);
    }
    case EByteCodeInstructions::Add_f32:
    {
        CALCULATE_NUMBER(+,float);
    }
    case EByteCodeInstructions::Add_f64:
    {
        CALCULATE_NUMBER(+,double);
    }

    case EByteCodeInstructions::Add_String:
    {
        auto Right = ValueStack.top();
        ValueStack.pop();
        auto Left = ValueStack.top();
        ValueStack.pop();

        CString LeftString = AnyCast<CString>(Left);
        CString RightString = AnyCast<CString>(Right);

        ValueStack.emplace(LeftString + RightString);

        GoNextLine();
        break;
    }

    case EByteCodeInstructions::And:
    {
        COMPARE_NUM(&&,bool);
    }

    case EByteCodeInstructions::Box:
    {
        auto GSharpBoxedValue = NewObject<RGSharpBoxedValue>(RScriptSubsystem::Get());
        GSharpBoxedValue->Value = ValueStack.top();
        ValueStack.pop();

        CByteCodeFile::STypeInfo TypeCode;
        *ScriptFileReader>>TypeCode;

        ValueStack.push(GSharpBoxedValue);

        GoNextLine();
        break;
    }

    case EByteCodeInstructions::Br:
    {
        uint32_t LineNumber;
        *ScriptFileReader>>LineNumber;

        GoLine(LineNumber);
        break;
    }
    
    case EByteCodeInstructions::BrEqual:
    {
        auto Right = ValueStack.top();
        ValueStack.pop();

        auto Left = ValueStack.top();
        ValueStack.pop();

        uint32_t JumpTargetLine;
        *ScriptFileReader>>JumpTargetLine;

        CType* LeftType=Left.GetType();
        CType* RightType=Right.GetType();
        assert(LeftType==RightType);

        if(LeftType->IsInstanceEqual(Left.GetPointer(),Right.GetPointer()))
        {
            GoLine(JumpTargetLine);
        }
        else
        {
            GoNextLine();
        }
        break;
    }

    case EByteCodeInstructions::BrFalse:
    {
        auto BooleanInt =std::move(ValueStack.top());
        ValueStack.pop();

        bool Value = AnyCast<bool>(BooleanInt);

        if(Value!=0)
        {
            GoNextLine();
            break;
        }
        else
        {
            uint32_t LineNumber;
            *ScriptFileReader>>LineNumber;

            GoLine(LineNumber);
            break;            
        }
    }

    case EByteCodeInstructions::BrTrue:
    {
        auto BooleanInt =std::move(ValueStack.top());
        ValueStack.pop();

        bool Value = AnyCast<bool>(BooleanInt);

        if(Value==0)
        {
            GoNextLine();
            break;
        }
        else
        {
            uint32_t LineNumber;
            *ScriptFileReader>>LineNumber;

            GoLine(LineNumber);
            break;            
        }            

    }

        case EByteCodeInstructions::Duplicate:
        {
            ValueStack.push(ValueStack.top());
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i8:
        {
            int8_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i16:
        {
            int16_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i32:
        {
            int32_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64:
        {
            int64_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_1:
        {
            ValueStack.push((int64_t)1);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_2:
        {
            ValueStack.push((int64_t)2);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_3:
        {
            ValueStack.push((int64_t)3);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_4:
        {
            ValueStack.push((int64_t)4);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_5:
        {
            ValueStack.push((int64_t)5);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_6:
        {
            ValueStack.push((int64_t)6);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_7:
        {
            ValueStack.push((int64_t)7);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_8:
        {
            ValueStack.push((int64_t)8);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_0:
        {
            ValueStack.push((int64_t)0);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_i64_m1:
        {
            ValueStack.push((int64_t)-1);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_u8:
        {
            uint8_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_u16:
        {
            uint16_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_u32:
        {
            uint32_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_u64:
        {
            uint64_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_r32:
        {
            float Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_r64:
        {
            double Value; 
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_False:
        {
            ValueStack.push(false);

            GoNextLine();
            break;
        }
        case EByteCodeInstructions::Ldc_True:
        {
            ValueStack.push(true);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldc_Char:
        {
            uint32_t Value; 
            *ScriptFileReader>>Value;
            ValueStack.push((char32_t)Value);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldstr:
        {
            CString Value ;
            *ScriptFileReader>>Value;
            ValueStack.push(Value);

            GoNextLine();
            break;
        }

        

        case EByteCodeInstructions::Ldloc:
        {
            uint32_t LocalVarialbeIndex;
            *ScriptFileReader>>LocalVarialbeIndex;

            ValueStack.emplace(VariableStack[LocalVarialbeIndex]);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldloca:
        {
            uint32_t LocalVarialbeIndex;
            *ScriptFileReader>>LocalVarialbeIndex;

            CAny & Variable = VariableStack[LocalVarialbeIndex];
            
            CPointerGeneralized* PointerType =WH::FindOrCreatePointerType(Variable.GetType());
            void* VariablePointer=  PointerType->CreateInstance(Variable.GetPointer());
            
            ValueStack.emplace(PointerType,VariablePointer);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Stloc:
        {
            uint32_t LocalVarialbeIndex;
            *ScriptFileReader>>LocalVarialbeIndex;

            VariableStack[LocalVarialbeIndex] =std::move(ValueStack.top());
            ValueStack.pop();

            GoNextLine();
            break;
        }

    
    
        case EByteCodeInstructions::Sub_i8:
        {
            CALCULATE_NUMBER(-,int8_t);
        }
        case EByteCodeInstructions::Sub_i16:
        {
            CALCULATE_NUMBER(-,int16_t);
        }
        case EByteCodeInstructions::Sub_i32:
        {
            CALCULATE_NUMBER(-,int32_t);
        }
        case EByteCodeInstructions::Sub_i64:
        {
            CALCULATE_NUMBER(-,int64_t);
        }
        case EByteCodeInstructions::Sub_u8:
        {
            CALCULATE_NUMBER(-,uint8_t);
        }
        case EByteCodeInstructions::Sub_u16:
        {
            CALCULATE_NUMBER(-,uint16_t);
        }
        case EByteCodeInstructions::Sub_u32:
        {
            CALCULATE_NUMBER(-,uint32_t);
        }
        case EByteCodeInstructions::Sub_u64:
        {
            CALCULATE_NUMBER(-,uint64_t);
        }
        case EByteCodeInstructions::Sub_f32:
        {
            CALCULATE_NUMBER(-,float);
        }
        case EByteCodeInstructions::Sub_f64:
        {
            CALCULATE_NUMBER(-,double);
        }

        case EByteCodeInstructions::Multiply_i8:
        {
            CALCULATE_NUMBER(*,int8_t);
        }
        case EByteCodeInstructions::Multiply_i16:
        {
            CALCULATE_NUMBER(*,int16_t);
        }
        case EByteCodeInstructions::Multiply_i32:
        {
            CALCULATE_NUMBER(*,int32_t);
        }
        case EByteCodeInstructions::Multiply_i64:
        {
            CALCULATE_NUMBER(*,int64_t);
        }
        case EByteCodeInstructions::Multiply_u8:
        {
            CALCULATE_NUMBER(*,uint8_t);
        }
        case EByteCodeInstructions::Multiply_u16:
        {
            CALCULATE_NUMBER(*,uint16_t);
        }
        case EByteCodeInstructions::Multiply_u32:
        {
            CALCULATE_NUMBER(*,uint32_t);
        }
        case EByteCodeInstructions::Multiply_u64:
        {
            CALCULATE_NUMBER(*,uint64_t);
        }
        case EByteCodeInstructions::Multiply_f32:
        {
            CALCULATE_NUMBER(*,float);
        }
        case EByteCodeInstructions::Multiply_f64:
        {
            CALCULATE_NUMBER(*,double);
        }

        case EByteCodeInstructions::Divide_i8:
        {
            CALCULATE_NUMBER(/,int8_t);
        }
        case EByteCodeInstructions::Divide_i16:
        {
            CALCULATE_NUMBER(/,int16_t);
        }
        case EByteCodeInstructions::Divide_i32:
        {
            CALCULATE_NUMBER(/,int32_t);
        }
        case EByteCodeInstructions::Divide_i64:
        {
            CALCULATE_NUMBER(/,int64_t);
        }
        case EByteCodeInstructions::Divide_u8:
        {
            CALCULATE_NUMBER(/,uint8_t);
        }
        case EByteCodeInstructions::Divide_u16:
        {
            CALCULATE_NUMBER(/,uint16_t);
        }
        case EByteCodeInstructions::Divide_u32:
        {
            CALCULATE_NUMBER(/,uint32_t);
        }
        case EByteCodeInstructions::Divide_u64:
        {
            CALCULATE_NUMBER(/,uint64_t);
        }
        case EByteCodeInstructions::Divide_f32:
        {
            CALCULATE_NUMBER(/,float);
        }
        case EByteCodeInstructions::Divide_f64:
        {
            CALCULATE_NUMBER(/,double);
        }

        case EByteCodeInstructions::Mod_i8:
        {
            CALCULATE_NUMBER(%,int8_t);
        }
        case EByteCodeInstructions::Mod_i16:
        {
            CALCULATE_NUMBER(%,int16_t);
        }
        case EByteCodeInstructions::Mod_i32:
        {
            CALCULATE_NUMBER(%,int32_t);
        }
        case EByteCodeInstructions::Mod_i64:
        {
            CALCULATE_NUMBER(%,int64_t);
        }
        case EByteCodeInstructions::Mod_u8:
        {
            CALCULATE_NUMBER(%,uint8_t);
        }
        case EByteCodeInstructions::Mod_u16:
        {
            CALCULATE_NUMBER(%,uint16_t);
        }
        case EByteCodeInstructions::Mod_u32:
        {
            CALCULATE_NUMBER(%,uint32_t);
        }
        case EByteCodeInstructions::Mod_u64:
        {
            CALCULATE_NUMBER(%,uint64_t);
        }

        case EByteCodeInstructions::Pop:
        {
            ValueStack.pop();
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldarg:
        {
            uint32_t ArgumentIndex;
            *ScriptFileReader>>ArgumentIndex;

            ValueStack.emplace(ArgumentStack[ArgumentIndex]);
            //ValueStack.push(ArgumentStack[ArgumentIndex]);

            GoNextLine();

            break;
        }

        case EByteCodeInstructions::Ldarga:
        {
            uint32_t ArgumentIndex;
            *ScriptFileReader>>ArgumentIndex;

            CAny & Argument = ArgumentStack[ArgumentIndex];
            
            CPointerGeneralized* PointerType =WH::FindOrCreatePointerType(Argument.GetType());
            void* ArgPointer=  PointerType->CreateInstance(Argument.GetPointer());

            ValueStack.emplace(PointerType,ArgPointer);
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Starg:
        {
            uint32_t ArgumentIndex;
            *ScriptFileReader>>ArgumentIndex;

            ArgumentStack[ArgumentIndex]=std::move(ValueStack.top());
            ValueStack.pop();

            GoNextLine();

            break;
        }

        case EByteCodeInstructions::Call:
        {
            CByteCodeFile::SFunctionCallInfo FunctionCallInfo;
            *ScriptFileReader>>FunctionCallInfo;

            //find Type
            CType* Type = CType::FindType(FunctionCallInfo.Class);  // ScriptFileReader->ParseTypeInfo(FunctionCallInfo.Class);
            assert(Type!=nullptr&&"Type not found");

            if(Type->IsSharedObjectPtrType())
            {
                Type= reinterpret_cast<CObjectPtrTypeGeneralized*>(Type)->GetObjectClass();
            }

            //find function
            CFunction* Function = Type->Functions->Functions[FunctionCallInfo.Function];

            CallFunction(Function);

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::CallVirt:
        {
            CByteCodeFile::SFunctionCallInfo FunctionCallInfo;
            *ScriptFileReader>>FunctionCallInfo;
            
            //find class
            CFunction* OverridedFunction=nullptr;
            {

                CType* BaseClass = CType::FindType(FunctionCallInfo.Class);//ScriptFileReader->ParseTypeInfo(FunctionCallInfo.Class);
                assert(BaseClass!=nullptr  && "class not found");
                
                if(BaseClass->IsSharedObjectPtrType())
                {
                    BaseClass= reinterpret_cast<CObjectPtrTypeGeneralized*>(BaseClass)->GetObjectClass();
                }
                
                assert(BaseClass->IsClassType());

                SFunctionID BaseFunctionID = BaseClass->Functions->VirtualFunctionTable[FunctionCallInfo.Function];
                OverridedFunction = SFunctionID::FindFunction(BaseFunctionID); 
            }
            
            //push arguments
            TVector<CAny> Arguments;
            Arguments.Resize(OverridedFunction->GetParameterNum()+1);

            for(int i=int(Arguments.Num())-1  ;i>=0;--i)
            {
                Arguments[i]=std::move(ValueStack.top());
                ValueStack.pop();
            }

            CType * ThisType = Arguments[0].GetType();
            //void* ThisPointer = Arguments[0].GetPointer();

            if(ThisType->IsRawPointerType())
            {
                CPointerGeneralized* PointerType= reinterpret_cast<CPointerGeneralized*>(ThisType);
                ThisType= PointerType->GetValueType().BasicType;
                //ThisPointer= PointerType->GetPointedObject(ThisPointer);
                assert(ThisType->WasTreatAsValueTypeInScript());
            }

            if(ThisType->IsSmartObjectPtr())
            {
                CObjectPtrTypeGeneralized* ObjectPtrType = reinterpret_cast<CObjectPtrTypeGeneralized*>(ThisType);
                ThisType=ObjectPtrType->GetObjectClass();
            }

            SFunctionID DerivedFunctionID=ThisType->Functions->VirtualFunctionTable[FunctionCallInfo.Function];
            CFunction* DerivedFunction=SFunctionID::FindFunction(DerivedFunctionID);

            auto && ReturnValue = DerivedFunction->Invoke(Arguments);

            if(!DerivedFunction->GetReturnType().BasicType->IsVoidType())
            {
                ValueStack.push(ReturnValue);
            }

            GoNextLine();

            break;
        }

        case EByteCodeInstructions::CastClass:
        {
            STypeID TypeID;
            *ScriptFileReader>>TypeID;

            CClass* TargetClass = nullptr;
            {
                CType* TargetType = CType::FindType(TypeID); //ScriptFileReader->ParseTypeInfo(TypeCode);
                assert(TargetType->IsSharedObjectPtrType()&&"assume compiler give a shared_ptr type");
                TargetClass = reinterpret_cast<CObjectPtrTypeGeneralized*>(TargetType)->GetObjectClass();
            }

            CAny& AnyValue = ValueStack.top();

            CType* ValueType = AnyValue.GetType();

            if(ValueType->IsRawPointerType())
            {
                CPointerGeneralized* PointerType= reinterpret_cast<CPointerGeneralized*>(ValueType);
                ValueType= PointerType->GetValueType().BasicType;
            }

            if(ValueType->IsSmartObjectPtr())
            {
                CObjectPtrTypeGeneralized* ObjectPtrType = reinterpret_cast<CObjectPtrTypeGeneralized*>(ValueType);
                ValueType=ObjectPtrType->GetObjectClass();
            }

            assert(ValueType->IsClassType()&&"Value type must be a class type");
            CClass * ValueClass = reinterpret_cast<CClass*>(ValueType);
            if(!ValueClass->IsA(TargetClass))
            {
                //throw exception
                ThrowException(
                    U"Cant cast class from \""
                    +ValueClass->GetFullName()+U"\" to \""
                    +TargetClass->GetFullName()+U"\"");
            }
            
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::LoadFunction:
        {
            CByteCodeFile::SFunctionLoadInfo FunctionLoadInfo;
            *ScriptFileReader>>FunctionLoadInfo;

            //find Type
            CType* Type = CType::FindType(FunctionLoadInfo.Class);  //ScriptFileReader->ParseTypeInfo(FunctionLoadInfo.Class);
            assert(Type!=nullptr&&"Type not found");

            if(Type->IsSharedObjectPtrType())
            {
                Type= reinterpret_cast<CObjectPtrTypeGeneralized*>(Type)->GetObjectClass();
            }

            //find function
            CFunction* Function = Type->Functions->Functions[FunctionLoadInfo.Function];            
            ValueStack.emplace(Function->GetFunctionType(),new CFunction*(Function));

            GoNextLine();

            break;
        }

        case EByteCodeInstructions::LoadNullPtr:
        {
            ValueStack.emplace(nullptr);

            GoNextLine();
            break;

        }
        case EByteCodeInstructions::LoadNullObject:
        {
            ValueStack.emplace(
                CNullObjectPtr::GetType()
                ,CNullObjectPtr::GetType()->CreateInstance()
            );

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Init_ValueType:
        {
            STypeID ValueTypeID;
            *ScriptFileReader>>ValueTypeID;

            auto ValuePointerAny=std::move(ValueStack.top());
            ValueStack.pop();

            //must be  address of value type
            assert(ValuePointerAny.GetType()->IsRawPointerType());

            CPointerGeneralized* PointerType= reinterpret_cast<CPointerGeneralized*>(ValuePointerAny.GetType());
            CType* ValueType = PointerType->GetValueType().BasicType;
            void* Value= PointerType->GetPointedObject(ValuePointerAny.GetPointer());
            
            ValueType->ResetAllocated(Value);

            //init the value at top of stack
            //don't need change the value stack
            // ValueStack.top().GetType()->
            //     ResetAllocated(ValueStack.top().GetPointer());

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Negative_i8:
        {
            Negative_NUMBER(int8_t);
        }

        case EByteCodeInstructions::Negative_i16:
        {
            Negative_NUMBER(int16_t);
        }

        case EByteCodeInstructions::Negative_i32:
        {
            Negative_NUMBER(int32_t);
        }

        case EByteCodeInstructions::Negative_i64:
        {
            Negative_NUMBER(int64_t);
        }

        case EByteCodeInstructions::Negative_u8:
        {
            Negative_NUMBER(uint8_t);
        }

        case EByteCodeInstructions::Negative_u16:
        {
            Negative_NUMBER(uint16_t);
        }

        case EByteCodeInstructions::Negative_u32:
        {
            Negative_NUMBER(uint32_t);
        }

        case EByteCodeInstructions::Negative_u64:
        {
            Negative_NUMBER(uint64_t);
        }

        case EByteCodeInstructions::Negative_f32:
        {
            Negative_NUMBER(float);
        }

        case EByteCodeInstructions::Negative_f64:
        {
            Negative_NUMBER(double);
        }

    
        case EByteCodeInstructions::New_Object:
        {
            CByteCodeFile::SFunctionCallInfo ConstructorCallInfo;
            *ScriptFileReader>>ConstructorCallInfo;

            //find class
            CType* SmartPointerType  = CType::FindType(ConstructorCallInfo.Class);// ScriptFileReader->ParseTypeInfo(ConstructorCallInfo.Class);

            assert(SmartPointerType!=nullptr
                &&SmartPointerType->IsSmartObjectPtr()
            &&"class not found");


            CClass* Class = reinterpret_cast<CObjectPtrTypeGeneralized*>(SmartPointerType)->GetObjectClass();

            RObject* Object=NewObjectInner(Class,RScriptSubsystem::Get());
            
            //copy a shared_ptr
            TSharedObjectPtr<RObject>* ObjectSmartPtr= 
                static_cast<TSharedObjectPtr<RObject>*>(
                    SmartPointerType->CreateInstance());

            reinterpret_cast<CObjectPtrTypeGeneralized*>(SmartPointerType)
                ->SetObjectPtr(ObjectSmartPtr,Object);
          

            CAny ObjectAny(SmartPointerType,ObjectSmartPtr);

            if(ConstructorCallInfo.Function==0
                && !Class->IsScriptClassType()
            )
            {
                //this is a native class
                //do not need to call constructor
            }
            else
            {
                //find function
                CFunction* Function = Class->Functions->Functions[ConstructorCallInfo.Function];

                //push arguments
                TVector<CAny> Arguments;
                Arguments.Resize(Function->GetParameterNum()+1);
                Arguments[0]=ObjectAny;

                
                for(int i=int(Function->GetParameterNum())-1  ;i>=0;--i)
                {
                    Arguments[i+1]=std::move(ValueStack.top());
                    ValueStack.pop();
                }
                Function->Invoke(Arguments);
            }

            ValueStack.push(ObjectAny);
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Stfld:
        {
            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;

            auto Value = std::move(ValueStack.top());
            ValueStack.pop();

            auto ObjectAny = std::move(ValueStack.top());
            ValueStack.pop();
                
            CType* ObjectType= ObjectAny.GetType();
            void* ObjectPtr = ObjectAny.GetPointer();
            
            if(ObjectType->IsRawPointerType())
            {
                //this is a raw pointer
                CPointerGeneralized* RawPointerType = reinterpret_cast<CPointerGeneralized*>(ObjectType);
                ObjectType = RawPointerType->GetValueType().BasicType;
                ObjectPtr = RawPointerType->GetPointedObject(ObjectPtr);
            }

            if(ObjectType->IsSharedObjectPtrType())
            {
                //convert to shared ptr type 
                CObjectPtrTypeGeneralized* SharedPtrType 
                    = reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType);

                auto Object = SharedPtrType->GetObjectPtr(ObjectPtr);


                CClass* ObjectClass = SharedPtrType->GetObjectClass();

                auto Property= ObjectClass->Properties->GetNonStaticFieldProperties()[FieldIndex];
                ObjectClass->Properties->GetNonStaticFieldProperties()[FieldIndex]
                    ->NonStaticProperty->SetValue(Object ,std::move(Value));

                auto Property2=Property;
                
            }
            else
            {
                //for value type
                ObjectType->Properties->GetNonStaticFieldProperties()[FieldIndex]
                    ->NonStaticProperty->SetValue(ObjectPtr,std::move(Value));
            }

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldfld:
        {
            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;


            auto ObjectAny = std::move(ValueStack.top());
            ValueStack.pop();
                
            CType* ObjectType= ObjectAny.GetType();
            void* ObjectPtr = ObjectAny.GetPointer();
            
            if(ObjectType->IsRawPointerType())
            {
                //this is a raw pointer
                CPointerGeneralized* RawPointerType = reinterpret_cast<CPointerGeneralized*>(ObjectType);
                ObjectType = RawPointerType->GetValueType().BasicType;
                ObjectPtr = RawPointerType->GetPointedObject(ObjectPtr);
            }

            if(ObjectType->IsSharedObjectPtrType())
            {
                //convert to shared ptr type 
                CObjectPtrTypeGeneralized* SharedPtrType 
                    = reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType);

                auto Object =SharedPtrType->GetObjectPtr(ObjectPtr);
                CClass* ObjectClass = SharedPtrType->GetObjectClass();

                CAny Value = ObjectClass->Properties->GetNonStaticFieldProperties()[FieldIndex]
                    ->NonStaticProperty->GetValue(Object);

                ValueStack.emplace(std::move(Value));
            }
            else
            {
                //for value type
                CAny Value = ObjectType->Properties->GetNonStaticFieldProperties()[FieldIndex]
                    ->NonStaticProperty->GetValue(ObjectPtr);
                ValueStack.emplace(std::move(Value));
            }

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldflda:
        {
            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;

            auto ObjectAny = std::move(ValueStack.top());
            ValueStack.pop();
                
            CType* ObjectType= ObjectAny.GetType();
            void* ObjectPtr = ObjectAny.GetPointer();

            if(ObjectType->IsRawPointerType())
            {
                //this is a raw pointer
                CPointerGeneralized* RawPointerType = reinterpret_cast<CPointerGeneralized*>(ObjectType);
                ObjectType = RawPointerType->GetValueType().BasicType;
                ObjectPtr = RawPointerType->GetPointedObject(ObjectPtr);
            }

            if(ObjectType->IsSharedObjectPtrType())
            {
                //convert to shared ptr type 
                CObjectPtrTypeGeneralized* SharedPtrType 
                    = reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType);

                auto Object =SharedPtrType->GetObjectPtr(ObjectPtr);
                CClass* ObjectClass = SharedPtrType->GetObjectClass();

                auto Field= ObjectClass->Properties->GetNonStaticFieldProperties()[FieldIndex];

                CType* FieldType = Field->GetType().BasicType;
                void* FieldValuePtr = Field->FieldProperty->GetValuePointer(Object);

                CPointerGeneralized* FieldPtrType = WH::FindOrCreatePointerType(FieldType);
                void* FieldValuePtrPtr= FieldPtrType->CreateInstance(FieldValuePtr);

                CAny Value(FieldPtrType, FieldValuePtrPtr);

                ValueStack.emplace(std::move(Value));
            }
            else
            {
                //for value type
                auto Field= ObjectType->Properties->GetNonStaticFieldProperties()[FieldIndex];

                CType* FieldType = Field->GetType().BasicType;
                void* FieldValuePtr = Field->FieldProperty->GetValuePointer(ObjectPtr);
                
                CPointerGeneralized* FieldPtrType = WH::FindOrCreatePointerType(FieldType);
                void* FieldValuePtrPtr = FieldPtrType->CreateInstance(FieldValuePtr);

                CAny Value(FieldPtrType, FieldValuePtrPtr);

                ValueStack.emplace(std::move(Value));
            }

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Stsfld:
        {
            STypeID TypeID;
            *ScriptFileReader>>TypeID;

            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;

            auto Value = std::move(ValueStack.top());
            ValueStack.pop();

            CType* ObjectType = CType::FindType(TypeID);// ScriptFileReader->ParseTypeInfo(TypeInfo);
            if(ObjectType->IsSharedObjectPtrType())
            {
                ObjectType= reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType)->GetObjectClass();
            }

            ObjectType->Properties->GetStaticFieldProperties()[FieldIndex]
                    ->StaticProperty->SetValue(std::move(Value));

            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldsfld:
        {
            STypeID TypeID;
            *ScriptFileReader>>TypeID;  

            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;

            CType* ObjectType =CType::FindType(TypeID);// ScriptFileReader->ParseTypeInfo(TypeInfo);
            if(ObjectType->IsSharedObjectPtrType())
            {
                ObjectType= reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType)->GetObjectClass();
            }

            CAny Value=ObjectType->Properties->GetStaticFieldProperties()[FieldIndex]
                    ->StaticProperty->GetValue();

            ValueStack.emplace(std::move(Value));
           
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Ldsflda:
        {
            STypeID TypeID;
            *ScriptFileReader>>TypeID;  

            uint32_t FieldIndex;
            *ScriptFileReader>>FieldIndex;

            CType* ObjectType =CType::FindType(TypeID);//  ScriptFileReader->ParseTypeInfo(TypeInfo);
            if(ObjectType->IsSharedObjectPtrType())
            {
                ObjectType= reinterpret_cast<CObjectPtrTypeGeneralized*>(ObjectType)->GetObjectClass();
            }

            auto Field= ObjectType->Properties->GetStaticFieldProperties()[FieldIndex];

            CType* FieldType = Field->GetType().BasicType;
            const void* FieldValuePtr = Field->FieldProperty->GetStaticValuePointer();

            CPointerGeneralized* FieldPtrType = WH::FindOrCreatePointerType(FieldType);
            void* FieldValuePtrPtr= FieldPtrType->CreateInstance(FieldValuePtr);

            CAny Value(FieldPtrType, FieldValuePtrPtr);
            ValueStack.emplace(std::move(Value));

            GoNextLine();
            break;

        }

        case EByteCodeInstructions::Ret:
        {
            GoEndLine();
            break;
        }

        case EByteCodeInstructions::Nop:
        {
            GoNextLine();
            break;
        }

        case EByteCodeInstructions::Or:
        {
            COMPARE_NUM(||,bool);
        }

        case EByteCodeInstructions::Compare_Equal_i8:
        {
            COMPARE_NUM(==,int8_t);
        }
        case EByteCodeInstructions::Compare_Equal_i16:
        {
            COMPARE_NUM(==,int16_t);
        }
        case EByteCodeInstructions::Compare_Equal_i32:
        {
            COMPARE_NUM(==,int32_t);
        }
        case EByteCodeInstructions::Compare_Equal_i64:
        {
            COMPARE_NUM(==,int64_t);
        }
        case EByteCodeInstructions::Compare_Equal_u8:
        {
            COMPARE_NUM(==,uint8_t);
        }
        case EByteCodeInstructions::Compare_Equal_u16:
        {
            COMPARE_NUM(==,uint16_t);
        }
        case EByteCodeInstructions::Compare_Equal_u32:
        {
            COMPARE_NUM(==,uint32_t);
        }
        case EByteCodeInstructions::Compare_Equal_u64:
        {
            COMPARE_NUM(==,uint64_t);
        }
        case EByteCodeInstructions::Compare_Equal_f32:
        {
            COMPARE_NUM(==,float);
        }
        case EByteCodeInstructions::Compare_Equal_f64:
        {
            COMPARE_NUM(==,double);
        }
        case EByteCodeInstructions::Compare_Equal_Bool:
        {
            COMPARE_NUM(==,bool);
        }
        case EByteCodeInstructions::Compare_Equal_Char:
        {
            COMPARE_NUM(==,char32_t);
        }
        case EByteCodeInstructions::Compare_Equal_String:
        {
            COMPARE_NUM(==,CString);
        }
        case EByteCodeInstructions::Compare_Equal_Object:
        {
            TSharedObjectPtr<RObject> Right = AnyCast<TSharedObjectPtr<RObject>>(ValueStack.top()); 
            ValueStack.pop();  
            TSharedObjectPtr<RObject> Left = AnyCast<TSharedObjectPtr<RObject>>(ValueStack.top()); 
            ValueStack.pop(); 
            ValueStack.push((bool)(Left == Right));  
            GoNextLine(); 
            break; 
        }

        case EByteCodeInstructions::Compare_NotEqual_i8:
        {
            COMPARE_NUM(!=,int8_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_i16:
        {
            COMPARE_NUM(!=,int16_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_i32:
        {
            COMPARE_NUM(!=,int32_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_i64:
        {
            COMPARE_NUM(!=,int64_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_u8:
        {
            COMPARE_NUM(!=,uint8_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_u16:
        {
            COMPARE_NUM(!=,uint16_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_u32:
        {
            COMPARE_NUM(!=,uint32_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_u64:
        {
            COMPARE_NUM(!=,uint64_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_f32:
        {
            COMPARE_NUM(!=,float);
        }
        case EByteCodeInstructions::Compare_NotEqual_f64:
        {
            COMPARE_NUM(!=,double);
        }
        case EByteCodeInstructions::Compare_NotEqual_Bool:
        {
            COMPARE_NUM(!=,bool);
        }
        case EByteCodeInstructions::Compare_NotEqual_Char:
        {
            COMPARE_NUM(!=,char32_t);
        }
        case EByteCodeInstructions::Compare_NotEqual_String:
        {
            COMPARE_NUM(!=,CString);
        }
        case EByteCodeInstructions::Compare_NotEqual_Object:
        {
            COMPARE_NUM(!=,TSharedObjectPtr<RObject>);
        }

        case EByteCodeInstructions::Compare_Less_i8:
        {
            COMPARE_NUM(<,int8_t);
        }
        case EByteCodeInstructions::Compare_Less_i16:
        {
            COMPARE_NUM(<,int16_t);
        }
        case EByteCodeInstructions::Compare_Less_i32:
        {
            COMPARE_NUM(<,int32_t);
        }
        case EByteCodeInstructions::Compare_Less_i64:
        {
            COMPARE_NUM(<,int64_t);
        }
        case EByteCodeInstructions::Compare_Less_u8:
        {
            COMPARE_NUM(<,uint8_t);
        }
        case EByteCodeInstructions::Compare_Less_u16:
        {
            COMPARE_NUM(<,uint16_t);
        }
        case EByteCodeInstructions::Compare_Less_u32:
        {
            COMPARE_NUM(<,uint32_t);
        }
        case EByteCodeInstructions::Compare_Less_u64:
        {
            COMPARE_NUM(<,uint64_t);
        }
        case EByteCodeInstructions::Compare_Less_f32:
        {
            COMPARE_NUM(<,float);
        }
        case EByteCodeInstructions::Compare_Less_f64:
        {
            COMPARE_NUM(<,double);
        }
        
        case EByteCodeInstructions::Compare_LessEqual_i8:
        {
            COMPARE_NUM(<=,int8_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_i16:
        {
            COMPARE_NUM(<=,int16_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_i32:
        {
            COMPARE_NUM(<=,int32_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_i64:
        {
            COMPARE_NUM(<=,int64_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_u8:
        {
            COMPARE_NUM(<=,uint8_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_u16:
        {
            COMPARE_NUM(<=,uint16_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_u32:
        {
            COMPARE_NUM(<=,uint32_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_u64:
        {
            COMPARE_NUM(<=,uint64_t);
        }
        case EByteCodeInstructions::Compare_LessEqual_f32:
        {
            COMPARE_NUM(<=,float);
        }
        case EByteCodeInstructions::Compare_LessEqual_f64:
        {
            COMPARE_NUM(<=,double);
        }

        case EByteCodeInstructions::Compare_Greater_i8:
        {
            COMPARE_NUM(>,int8_t);
        }
        case EByteCodeInstructions::Compare_Greater_i16:
        {
            COMPARE_NUM(>,int16_t);
        }
        case EByteCodeInstructions::Compare_Greater_i32:
        {
            COMPARE_NUM(>,int32_t);
        }
        case EByteCodeInstructions::Compare_Greater_i64:
        {
            COMPARE_NUM(>,int64_t);
        }
        case EByteCodeInstructions::Compare_Greater_u8:
        {
            COMPARE_NUM(>,uint8_t);
        }
        case EByteCodeInstructions::Compare_Greater_u16:
        {
            COMPARE_NUM(>,uint16_t);
        }
        case EByteCodeInstructions::Compare_Greater_u32:
        {
            COMPARE_NUM(>,uint32_t);
        }
        case EByteCodeInstructions::Compare_Greater_u64:
        {
            COMPARE_NUM(>,uint64_t);
        }
        case EByteCodeInstructions::Compare_Greater_f32:
        {
            COMPARE_NUM(>,float);
        }
        case EByteCodeInstructions::Compare_Greater_f64:
        {
            COMPARE_NUM(>,double);
        }

        case EByteCodeInstructions::Compare_GreaterEqual_i8:
        {
            COMPARE_NUM(>=,int8_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_i16:
        {
            COMPARE_NUM(>=,int16_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_i32:
        {
            COMPARE_NUM(>=,int32_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_i64:
        {
            COMPARE_NUM(>=,int64_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_u8:
        {
            COMPARE_NUM(>=,uint8_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_u16:
        {
            COMPARE_NUM(>=,uint16_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_u32:
        {
            COMPARE_NUM(>=,uint32_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_u64:
        {
            COMPARE_NUM(>=,uint64_t);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_f32:
        {
            COMPARE_NUM(>=,float);
        }
        case EByteCodeInstructions::Compare_GreaterEqual_f64:
        {
            COMPARE_NUM(>=,double);
        }

        case EByteCodeInstructions::Convert_i8_To_i16:
        {
            CONVERT_INNERTYPE(int8_t,int16_t);
        }
        case EByteCodeInstructions::Convert_i8_To_i32:
        {
            CONVERT_INNERTYPE(int8_t,int32_t);
        }
        case EByteCodeInstructions::Convert_i8_To_i64:
        {
            CONVERT_INNERTYPE(int8_t,int64_t);
        }
        case EByteCodeInstructions::Convert_i8_To_u8:
        {
            CONVERT_INNERTYPE(int8_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_i8_To_u16:
        {
            CONVERT_INNERTYPE(int8_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_i8_To_u32:
        {
            CONVERT_INNERTYPE(int8_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_i8_To_u64:
        {
            CONVERT_INNERTYPE(int8_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_i8_To_r32:
        {
            CONVERT_INNERTYPE(int8_t,float);
        }
        case EByteCodeInstructions::Convert_i8_To_r64:
        {
            CONVERT_INNERTYPE(int8_t,double);
        }
        case EByteCodeInstructions::Convert_i8_To_Bool:
        {
            CONVERT_INNERTYPE(int8_t,bool);
        }
        case EByteCodeInstructions::Convert_i8_To_Char:
        {
            CONVERT_INNERTYPE(int16_t,char32_t);
        }
        case EByteCodeInstructions::Convert_i16_To_i8:
        {
            CONVERT_INNERTYPE(int16_t,int8_t);
        }
        case EByteCodeInstructions::Convert_i16_To_i32:
        {
            CONVERT_INNERTYPE(int16_t,int32_t);
        }
        case EByteCodeInstructions::Convert_i16_To_i64:
        {
            CONVERT_INNERTYPE(int16_t,int64_t);
        }
        case EByteCodeInstructions::Convert_i16_To_u8:
        {
            CONVERT_INNERTYPE(int16_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_i16_To_u16:
        {
            CONVERT_INNERTYPE(int16_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_i16_To_u32:
        {
            CONVERT_INNERTYPE(int16_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_i16_To_u64:
        {
            CONVERT_INNERTYPE(int16_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_i16_To_r32:
        {
            CONVERT_INNERTYPE(int16_t,float);
        }
        case EByteCodeInstructions::Convert_i16_To_r64:
        {
            CONVERT_INNERTYPE(int16_t,double);
        }
        case EByteCodeInstructions::Convert_i16_To_Bool:
        {
            CONVERT_INNERTYPE(int16_t,bool);
        }
        case EByteCodeInstructions::Convert_i16_To_Char:
        {
            CONVERT_INNERTYPE(int16_t,char32_t);
        }
        case EByteCodeInstructions::Convert_i32_To_i8:
        {
            CONVERT_INNERTYPE(int32_t,int8_t);
        }
        case EByteCodeInstructions::Convert_i32_To_i16:
        {
            CONVERT_INNERTYPE(int32_t,int16_t);
        }
        case EByteCodeInstructions::Convert_i32_To_i64:
        {
            CONVERT_INNERTYPE(int32_t,int64_t);
        }
        case EByteCodeInstructions::Convert_i32_To_u8:
        {
            CONVERT_INNERTYPE(int32_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_i32_To_u16:
        {
            CONVERT_INNERTYPE(int32_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_i32_To_u32:
        {
            CONVERT_INNERTYPE(int32_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_i32_To_u64:
        {
            CONVERT_INNERTYPE(int32_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_i32_To_r32:
        {
            CONVERT_INNERTYPE(int32_t,float);
        }
        case EByteCodeInstructions::Convert_i32_To_r64:
        {
            CONVERT_INNERTYPE(int32_t,double);
        }
        case EByteCodeInstructions::Convert_i32_To_Bool:
        {
            CONVERT_INNERTYPE(int32_t,bool);
        }
        case EByteCodeInstructions::Convert_i32_To_Char:
        {
            CONVERT_INNERTYPE(int32_t,char32_t);
        }

        case EByteCodeInstructions::Convert_i64_To_i8:
        {
            CONVERT_INNERTYPE(int64_t,int8_t);
        }
        case EByteCodeInstructions::Convert_i64_To_i16:
        {
            CONVERT_INNERTYPE(int64_t,int16_t);
        }
        case EByteCodeInstructions::Convert_i64_To_i32:
        {
            CONVERT_INNERTYPE(int64_t,int32_t);
        }
        case EByteCodeInstructions::Convert_i64_To_u8:
        {
            CONVERT_INNERTYPE(int64_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_i64_To_u16:
        {
            CONVERT_INNERTYPE(int64_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_i64_To_u32:
        {
            CONVERT_INNERTYPE(int64_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_i64_To_u64:
        {
            CONVERT_INNERTYPE(int64_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_i64_To_r32:
        {
            CONVERT_INNERTYPE(int64_t,float);
        }
        case EByteCodeInstructions::Convert_i64_To_r64:
        {
            CONVERT_INNERTYPE(int64_t,double);
        }
        case EByteCodeInstructions::Convert_i64_To_Bool:
        {
            CONVERT_INNERTYPE(int64_t,bool);
        }
        case EByteCodeInstructions::Convert_i64_To_Char:
        {
            CONVERT_INNERTYPE(int64_t,char32_t);
        }

        case EByteCodeInstructions::Convert_u8_To_i8:
        {
            CONVERT_INNERTYPE(uint8_t,int8_t);
        }
        case EByteCodeInstructions::Convert_u8_To_i16:
        {
            CONVERT_INNERTYPE(uint8_t,int16_t);
        }
        case EByteCodeInstructions::Convert_u8_To_i32:
        {
            CONVERT_INNERTYPE(uint8_t,int32_t);
        }
        case EByteCodeInstructions::Convert_u8_To_i64:
        {
            CONVERT_INNERTYPE(uint8_t,int64_t);
        }
        case EByteCodeInstructions::Convert_u8_To_u16:
        {
            CONVERT_INNERTYPE(uint8_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_u8_To_u32:
        {
            CONVERT_INNERTYPE(uint8_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_u8_To_u64:
        {
            CONVERT_INNERTYPE(uint8_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_u8_To_r32:
        {
            CONVERT_INNERTYPE(uint8_t,float);
        }
        case EByteCodeInstructions::Convert_u8_To_r64:
        {
            CONVERT_INNERTYPE(uint8_t,double);
        }
        case EByteCodeInstructions::Convert_u8_To_Bool:
        {
            CONVERT_INNERTYPE(uint8_t,bool);
        }
        case EByteCodeInstructions::Convert_u8_To_Char:
        {
            CONVERT_INNERTYPE(uint8_t,char32_t);
        }

        case EByteCodeInstructions::Convert_u16_To_i8:
        {
            CONVERT_INNERTYPE(uint16_t,int8_t);
        }
        case EByteCodeInstructions::Convert_u16_To_i16:
        {
            CONVERT_INNERTYPE(uint16_t,int16_t);
        }
        case EByteCodeInstructions::Convert_u16_To_i32:
        {
            CONVERT_INNERTYPE(uint16_t,int32_t);
        }
        case EByteCodeInstructions::Convert_u16_To_i64:
        {
            CONVERT_INNERTYPE(uint16_t,int64_t);
        }
        case EByteCodeInstructions::Convert_u16_To_u8:
        {
            CONVERT_INNERTYPE(uint16_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_u16_To_u32:
        {
            CONVERT_INNERTYPE(uint16_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_u16_To_u64:
        {
            CONVERT_INNERTYPE(uint16_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_u16_To_r32:
        {
            CONVERT_INNERTYPE(uint16_t,float);
        }
        case EByteCodeInstructions::Convert_u16_To_r64:
        {
            CONVERT_INNERTYPE(uint16_t,double);
        }
        case EByteCodeInstructions::Convert_u16_To_Bool:
        {
            CONVERT_INNERTYPE(uint16_t,bool);
        }
        case EByteCodeInstructions::Convert_u16_To_Char:
        {
            CONVERT_INNERTYPE(uint16_t,char32_t);
        }

        case EByteCodeInstructions::Convert_u32_To_i8:
        {
            CONVERT_INNERTYPE(uint32_t,int8_t);
        }
        case EByteCodeInstructions::Convert_u32_To_i16:
        {
            CONVERT_INNERTYPE(uint32_t,int16_t);
        }
        case EByteCodeInstructions::Convert_u32_To_i32:
        {
            CONVERT_INNERTYPE(uint32_t,int32_t);
        }
        case EByteCodeInstructions::Convert_u32_To_i64:
        {
            CONVERT_INNERTYPE(uint32_t,int64_t);
        }
        case EByteCodeInstructions::Convert_u32_To_u8:
        {
            CONVERT_INNERTYPE(uint32_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_u32_To_u16:
        {
            CONVERT_INNERTYPE(uint32_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_u32_To_u64:
        {
            CONVERT_INNERTYPE(uint32_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_u32_To_r32:
        {
            CONVERT_INNERTYPE(uint32_t,float);
        }
        case EByteCodeInstructions::Convert_u32_To_r64:
        {
            CONVERT_INNERTYPE(uint32_t,double);
        }
        case EByteCodeInstructions::Convert_u32_To_Bool:
        {
            CONVERT_INNERTYPE(uint32_t,bool);
        }
        case EByteCodeInstructions::Convert_u32_To_Char:
        {
            CONVERT_INNERTYPE(uint32_t,char32_t);
        }

        case EByteCodeInstructions::Convert_u64_To_i8:
        {
            CONVERT_INNERTYPE(uint64_t,int8_t);
        }
        case EByteCodeInstructions::Convert_u64_To_i16:
        {
            CONVERT_INNERTYPE(uint64_t,int16_t);
        }
        case EByteCodeInstructions::Convert_u64_To_i32:
        {
            CONVERT_INNERTYPE(uint64_t,int32_t);
        }
        case EByteCodeInstructions::Convert_u64_To_i64:
        {
            CONVERT_INNERTYPE(uint64_t,int64_t);
        }
        case EByteCodeInstructions::Convert_u64_To_u8:
        {
            CONVERT_INNERTYPE(uint64_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_u64_To_u16:
        {
            CONVERT_INNERTYPE(uint64_t,uint16_t);
        }
        case EByteCodeInstructions::Convert_u64_To_u32:
        {
            CONVERT_INNERTYPE(uint64_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_u64_To_r32:
        {
            CONVERT_INNERTYPE(uint64_t,float);
        }
        case EByteCodeInstructions::Convert_u64_To_r64:
        {
            CONVERT_INNERTYPE(uint64_t,double);
        }
        case EByteCodeInstructions::Convert_u64_To_Bool:
        {
            CONVERT_INNERTYPE(uint64_t,bool);
        }
        case EByteCodeInstructions::Convert_u64_To_Char:
        {
            CONVERT_INNERTYPE(uint64_t,char32_t);
        }

        case EByteCodeInstructions::Convert_r32_To_i8:
        {
            CONVERT_INNERTYPE(float,int8_t);
        }
        case EByteCodeInstructions::Convert_r32_To_i16:
        {
            CONVERT_INNERTYPE(float,int16_t);
        }
        case EByteCodeInstructions::Convert_r32_To_i32:
        {
            CONVERT_INNERTYPE(float,int32_t);
        }
        case EByteCodeInstructions::Convert_r32_To_i64:
        {
            CONVERT_INNERTYPE(float,int64_t);
        }
        case EByteCodeInstructions::Convert_r32_To_u8:
        {
            CONVERT_INNERTYPE(float,uint8_t);
        }
        case EByteCodeInstructions::Convert_r32_To_u16:
        {
            CONVERT_INNERTYPE(float,uint16_t);
        }
        case EByteCodeInstructions::Convert_r32_To_u32:
        {
            CONVERT_INNERTYPE(float,uint32_t);
        }
        case EByteCodeInstructions::Convert_r32_To_u64:
        {
            CONVERT_INNERTYPE(float,uint64_t);
        }
        case EByteCodeInstructions::Convert_r32_To_r64:
        {
            CONVERT_INNERTYPE(float,double);
        }
        case EByteCodeInstructions::Convert_r32_To_Bool:
        {
            CONVERT_INNERTYPE(float,bool);
        }
        case EByteCodeInstructions::Convert_r32_To_Char:
        {
            CONVERT_INNERTYPE(float,char32_t);
        }

        case EByteCodeInstructions::Convert_r64_To_i8:
        {
            CONVERT_INNERTYPE(double,int8_t);
        }
        case EByteCodeInstructions::Convert_r64_To_i16:
        {
            CONVERT_INNERTYPE(double,int16_t);
        }
        case EByteCodeInstructions::Convert_r64_To_i32:
        {
            CONVERT_INNERTYPE(double,int32_t);
        }
        case EByteCodeInstructions::Convert_r64_To_i64:
        {
            CONVERT_INNERTYPE(double,int64_t);
        }
        case EByteCodeInstructions::Convert_r64_To_u8:
        {
            CONVERT_INNERTYPE(double,uint8_t);
        }
        case EByteCodeInstructions::Convert_r64_To_u16:
        {
            CONVERT_INNERTYPE(double,uint16_t);
        }
        case EByteCodeInstructions::Convert_r64_To_u32:
        {
            CONVERT_INNERTYPE(double,uint32_t);
        }
        case EByteCodeInstructions::Convert_r64_To_u64:
        {
            CONVERT_INNERTYPE(double,uint64_t);
        }
        case EByteCodeInstructions::Convert_r64_To_r32:
        {
            CONVERT_INNERTYPE(double,float);
        }
        case EByteCodeInstructions::Convert_r64_To_Bool:
        {
            CONVERT_INNERTYPE(double,bool);
        }
        case EByteCodeInstructions::Convert_r64_To_Char:
        {
            CONVERT_INNERTYPE(double,char32_t);
        }

        case EByteCodeInstructions::Convert_Bool_To_i8:
        {
            CONVERT_INNERTYPE(bool,int8_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_i16:
        {
            CONVERT_INNERTYPE(bool,int16_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_i32:
        {
            CONVERT_INNERTYPE(bool,int32_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_i64:
        {
            CONVERT_INNERTYPE(bool,int64_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_u8:
        {
            CONVERT_INNERTYPE(bool,uint8_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_u16:
        {
            CONVERT_INNERTYPE(bool,uint16_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_u32:
        {
            CONVERT_INNERTYPE(bool,uint32_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_u64:
        {
            CONVERT_INNERTYPE(bool,uint64_t);
        }
        case EByteCodeInstructions::Convert_Bool_To_r32:
        {
            CONVERT_INNERTYPE(bool,float);
        }
        case EByteCodeInstructions::Convert_Bool_To_r64:
        {
            CONVERT_INNERTYPE(bool,double);
        }
        case EByteCodeInstructions::Convert_Bool_To_Char:
        {
            CONVERT_INNERTYPE(bool,char32_t);
        }

        case EByteCodeInstructions::Convert_Char_To_i8:
        {
            CONVERT_INNERTYPE(char32_t,int8_t);
        }
        case EByteCodeInstructions::Convert_Char_To_i16:
        {
            CONVERT_INNERTYPE(char32_t,int16_t);
        }
        case EByteCodeInstructions::Convert_Char_To_i32:
        {
            CONVERT_INNERTYPE(char32_t,int32_t);
        }
        case EByteCodeInstructions::Convert_Char_To_i64:
        {
            CONVERT_INNERTYPE(char32_t,int64_t);
        }
        case EByteCodeInstructions::Convert_Char_To_u8:
        {
            CONVERT_INNERTYPE(char32_t,uint8_t);
        }
        case EByteCodeInstructions::Convert_Char_To_u16:
        {
            CONVERT_INNERTYPE(char32_t,uint16_t);
        }

        case EByteCodeInstructions::Convert_Char_To_u32:
        {
            CONVERT_INNERTYPE(char32_t,uint32_t);
        }
        case EByteCodeInstructions::Convert_Char_To_u64:
        {
            CONVERT_INNERTYPE(char32_t,uint64_t);
        }
        case EByteCodeInstructions::Convert_Char_To_r32:
        {
            CONVERT_INNERTYPE(char32_t,float);
        }
        case EByteCodeInstructions::Convert_Char_To_r64:
        {
            CONVERT_INNERTYPE(char32_t,double);
        }
        case EByteCodeInstructions::Convert_Char_To_Bool:
        {
            CONVERT_INNERTYPE(char32_t,bool);
        }
        
        default:
            assert(false && "not implemented instruction");
            break;
    }

    return true;
}

void CFunctionInterpreter::CallFunction(CFunction *InFunction)
{
    //push arguments
    TVector<CAny> Arguments;
    uint64_t ArgumentCount;

    if(InFunction->bIsStatic)
    {
        ArgumentCount = InFunction->GetParameterNum();
    }
    else
    {
        ArgumentCount=InFunction->GetParameterNum()+1;               
    }

    Arguments.Resize(ArgumentCount);

    assert(ValueStack.size()>=ArgumentCount && "not enough arguments");

    CAny ObjectAny;

    if(InFunction->IsStatic())
    {
        for(int i=int(Arguments.size())-1  ;i>=0;--i)
        {
            Arguments[i]=std::move(ValueStack.top());
            ValueStack.pop();
        }
    }
    else
    {
        for(int i=int(Arguments.size())-1  ;i>0;--i)
        {
            Arguments[i]=std::move(ValueStack.top());
            ValueStack.pop();
        }

        //store the object into a local variable with longer life time 
        //after the function call
        //because might use it's raw pointer in the function call
        ObjectAny = std::move(ValueStack.top());
        ValueStack.pop();

        if(!ObjectAny.GetType()->IsRawPointerType() &&
           !ObjectAny.GetType()->IsSharedObjectPtrType())
        {
            //create a raw pointer
            CPointerGeneralized* RawPointerType = WH::FindOrCreatePointerType(ObjectAny.GetType());
            void* RawPointer = RawPointerType->CreateInstance(ObjectAny.GetPointer());

            Arguments[0]=CAny(RawPointerType,RawPointer);
        }
        else
        {
            Arguments[0]=std::move(ObjectAny);
        }
    }

    auto && ReturnValue = InFunction->Invoke(Arguments);

    if(!InFunction->GetReturnType().BasicType->IsVoidType())
    {
        ValueStack.push(ReturnValue);
    }

}

void CFunctionInterpreter::GoNextLine()
{
    LineIndex++;
}

void CFunctionInterpreter::GoEndLine()
{
    LineIndex= -1;
}

void CFunctionInterpreter::GoLine(uint32_t InLine)
{
    LineIndex=InLine;
}

void CFunctionInterpreter::ThrowException(const CString &InError)
{
    WH::PrintLine(U"Script Exception: ");
    WH::PrintLine(InError);

    assert(false && "Script Exception");

}

